/*
 * @(#)URLJarFile.java	1.7 06/10/10
 *
 * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.  
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER  
 *   
 * This program is free software; you can redistribute it and/or  
 * modify it under the terms of the GNU General Public License version  
 * 2 only, as published by the Free Software Foundation.   
 *   
 * This program is distributed in the hope that it will be useful, but  
 * WITHOUT ANY WARRANTY; without even the implied warranty of  
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU  
 * General Public License version 2 for more details (a copy is  
 * included at /legal/license.txt).   
 *   
 * You should have received a copy of the GNU General Public License  
 * version 2 along with this work; if not, write to the Free Software  
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  
 * 02110-1301 USA   
 *   
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa  
 * Clara, CA 95054 or visit www.sun.com if you need additional  
 * information or have any questions. 
 *
 */

package sun.net.www.protocol.jar;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
import java.security.cert.Certificate;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import sun.net.www.ParseUtil;

/* URL jar file is a common JarFile subtype used for JarURLConnection */
class URLJarFile extends JarFile {

    private static int BUF_SIZE = 2048;

    private Manifest superMan;
    private Attributes superAttr;
    private Map superEntries;

    static JarFile getJarFile(URL url) throws IOException {
	if (isFileURL(url))
	    return new URLJarFile(url);
	else {
	    return retrieve(url);
	}
    }

    private URLJarFile(File file) throws IOException {
	super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE);
    }
        
    private URLJarFile(URL url) throws IOException {
	super(ParseUtil.decode(url.getFile()));
    }
        
    private static boolean isFileURL(URL url) {
	return url.getProtocol().equalsIgnoreCase("file");
    }

    /*
     * close the jar file.
     */
    protected void finalize() throws IOException {
	close();
    }

    /**
     * Returns the <code>ZipEntry</code> for the given entry name or
     * <code>null</code> if not found.
     *
     * @param name the JAR file entry name
     * @return the <code>ZipEntry</code> for the given entry name or
     *         <code>null</code> if not found
     * @see java.util.zip.ZipEntry
     */
    public ZipEntry getEntry(String name) {
	ZipEntry ze = super.getEntry(name);
	if (ze != null) {
	    if (ze instanceof JarEntry) 
		return new URLJarFileEntry((JarEntry)ze);
	    else
		throw new InternalError(super.getClass() +
					" returned unexpected entry type " +
					ze.getClass());
	}
	return null;
    }

    public Manifest getManifest() throws IOException {

	if (!isSuperMan()) {
	    return null;
	}

	Manifest man = new Manifest();
	Attributes attr = man.getMainAttributes();
	attr.putAll((Map)superAttr.clone());
				
	// now deep copy the manifest entries
	if (superEntries != null) {
	    Map entries = man.getEntries();
	    Iterator it = superEntries.keySet().iterator();
	    while (it.hasNext()) {
		Object key = it.next();
		Attributes at = (Attributes)superEntries.get(key);
		entries.put(key, at.clone());
	    }
	}

	return man;
    }

    // optimal side-effects
    private synchronized boolean isSuperMan() throws IOException {

	if (superMan == null) {
	    superMan = super.getManifest();
	}

	if (superMan != null) {
	    superAttr = superMan.getMainAttributes();
	    superEntries = superMan.getEntries();
	    return true;
	} else 
	    return false;
    }

    /** 
     * Given a URL, retrieves a JAR file, caches it to disk, and creates a
     * cached JAR file object. 
     */
    private static JarFile retrieve(final URL url) throws IOException {
	JarFile result = null;

	/* get the stream before asserting privileges */
	final InputStream in =  url.openConnection().getInputStream();

	try { 
	    result = (JarFile)
		AccessController.doPrivileged(new PrivilegedExceptionAction() {
		    public Object run() throws IOException {
			OutputStream out = null;
			try {
			    File tmpFile = File.createTempFile("jar_cache", null);
			    tmpFile.deleteOnExit();
			    out  = new FileOutputStream(tmpFile);
			    int read = 0;
			    byte[] buf = new byte[BUF_SIZE];
			    while ((read = in.read(buf)) != -1) {
				out.write(buf, 0, read);
			    }
                            out.close();
			    out = null;
			    return new URLJarFile(tmpFile);
			} finally {
			    if (in != null) {
				in.close();
			    }
			    if (out != null) {
				out.close();
			    }
			}
		    }
		});
	} catch (PrivilegedActionException pae) {
	    throw (IOException) pae.getException();
	}

	return result;
    }

    private class URLJarFileEntry extends JarEntry {
	private JarEntry je;
	
	URLJarFileEntry(JarEntry je) {
	    super(je);
	    this.je=je;
	}

	public Attributes getAttributes() throws IOException {
	    if (URLJarFile.this.isSuperMan()) {
		Map e = URLJarFile.this.superEntries;
		if (e != null) {
		    Attributes a = (Attributes)e.get(getName());
		    if (a != null)
			return  (Attributes)a.clone();
		}
	    }
	    return null;
	}

	public java.security.cert.Certificate[] getCertificates() {
	    Certificate[] certs = je.getCertificates();
	    return certs == null? null: (Certificate[])certs.clone();
	}
    }
}
    

